package com.hdu.autoPageInterceptor;

import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.SystemMetaObject;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Map;

import static java.lang.String.format;

/**
 * 分页拦截器
 */
@Intercepts(
        @Signature(
                type = StatementHandler.class,
                method = "prepare",
                args = {Connection.class, Integer.class}
        )
)
public class PageInterceptor implements Interceptor {

    private final String COUNT_SQL_TEMPLATE = "select count(*) from (%s) as _page";
    private final String NEW_SQL_TEMPLATE = "%s offset %d, limit %d";

    @SuppressWarnings("all")
    @Override
    public Object intercept(Invocation invocation) throws Throwable {

        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        BoundSql boundSql = statementHandler.getBoundSql();
        Object parameterObject = boundSql.getParameterObject();

        Page page = null;

        if (parameterObject instanceof Page) {
            page = (Page) parameterObject;
        } else if (parameterObject instanceof Map) {
            page = (Page) ((Map) (parameterObject))
                    .values()
                    .stream()
                    .filter(p -> p instanceof Page)
                    .findFirst()
                    .orElse(null);
        }


        /**
         * 1. 检测当前是否需要分页条件
         */
        if (page == null) {
            return invocation.proceed();
        }
        /**
         * 2. 设置总行数 select count(*) from (sql)
         *
         */
        else  {
            page.setTotal(selectCount(invocation));
        }

        /**
         * 3. 修改原有sql (select * from user) offset 0, limit 50
         */
        String newSql = format(
                NEW_SQL_TEMPLATE,
                boundSql.getSql(),
                page.getOffset(),
                page.getSize()
        );
        SystemMetaObject.forObject(boundSql).setValue("sql", newSql);

        return invocation.proceed();
    }

    private int selectCount(Invocation invocation) throws SQLException {
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        BoundSql boundSql = statementHandler.getBoundSql();
        String sql = boundSql.getSql();
        String countSql = format(
                COUNT_SQL_TEMPLATE,
                sql
        );
        Connection connection = (Connection) invocation.getArgs()[0];
        PreparedStatement preparedStatement = connection.prepareStatement(countSql);
        // 设置参数
        statementHandler.getParameterHandler().setParameters(preparedStatement);
        ResultSet resultSet = preparedStatement.executeQuery();
        int totalCount = 0;
        if (resultSet.next()) {
            totalCount = resultSet.getInt(1);
        }

        resultSet.close();
        preparedStatement.close();

        return totalCount;
    }
}
